package TLS

import (
    "crypto/tls"
    "crypto/x509"
    "io/ioutil"
    "net"
)

type tls_srv struct {
    addr string

    ca_certs []string

    srv_certs []string
    srv_keys  []string

    client_certs []string

    client_auth tls.ClientAuthType
}

func NewTCPServer() *tls_srv {
    x := &tls_srv{}
    x.addr = "127.0.0.1:10000"
    x.client_auth = tls.RequireAndVerifyClientCert
    return x
}

func (s *tls_srv) WithBindAddress(addr string) *tls_srv {
    s.addr = addr
    return s
}

func (s *tls_srv) WithRootCertificate(cert string) *tls_srv {
    s.ca_certs = append(s.ca_certs, cert)
    return s
}

func (s *tls_srv) WithServerCertificate(cert, key string) *tls_srv {
    s.srv_certs = append(s.srv_certs, cert)
    s.srv_keys = append(s.srv_keys, key)
    return s
}

func (s *tls_srv) WithClientCertificate(cert string) *tls_srv {
    s.client_certs = append(s.client_certs, cert)
    return s
}

func (s *tls_srv) WithClientAuth(auth tls.ClientAuthType) *tls_srv {
    s.client_auth = auth
    return s
}

func (s *tls_srv) TLSConfig() *tls.Config {

    RootCAs := x509.NewCertPool()
    for _, cert := range s.ca_certs {
        pem_byte, err := ioutil.ReadFile(cert)
        if err != nil {
            continue
        }
        if !RootCAs.AppendCertsFromPEM(pem_byte) {
        }
    }

    var srv_certs []tls.Certificate
    for i := range s.srv_certs {
        cert, err := tls.LoadX509KeyPair(s.srv_certs[i], s.srv_keys[i])
        if err != nil {
            continue
        }
        srv_certs = append(srv_certs, cert)
    }

    var client_certpool = x509.NewCertPool()
    for i := range s.client_certs {
        certBytes, err := ioutil.ReadFile(s.client_certs[i])
        if err != nil {
            continue
        }
        if ok := client_certpool.AppendCertsFromPEM(certBytes); !ok {
            continue
        }
    }

    tls_config := &tls.Config{
        Certificates: srv_certs,
        ClientAuth:   s.client_auth,
        ClientCAs:    RootCAs,

        MinVersion: tls.VersionTLS12,
    }

    return tls_config
}

func (s *tls_srv) Listen() (net.Listener, error) {
    ln, err := tls.Listen("tcp", s.addr, s.TLSConfig())
    if err != nil {
        return nil, err
    }
    return ln, nil
}

type tls_client struct {
    ca_certs []string

    client_certs []string
    client_keys  []string

    srv_certs []string
}

func NewTCPClient() *tls_client {
    x := &tls_client{}
    return x
}

func (s *tls_client) WithRootCertificate(cert string) *tls_client {
    s.ca_certs = append(s.ca_certs, cert)
    return s
}

func (s *tls_client) WithClientCertificate(cert, key string) *tls_client {
    s.client_certs = append(s.client_certs, cert)
    s.client_keys = append(s.client_keys, key)
    return s
}

func (s *tls_client) WithServerCertificate(cert string) *tls_client {
    s.srv_certs = append(s.srv_certs, cert)
    return s
}

func (s *tls_client) TLSConfig() *tls.Config {

    RootCAs := x509.NewCertPool()
    for _, cert := range s.ca_certs {
        pem_byte, err := ioutil.ReadFile(cert)
        if err != nil {
            continue
        }
        if !RootCAs.AppendCertsFromPEM(pem_byte) {
        }
    }

    var client_certs []tls.Certificate
    for i := range s.client_certs {
        cert, err := tls.LoadX509KeyPair(s.client_certs[i], s.client_keys[i])
        if err != nil {
            continue
        }
        client_certs = append(client_certs, cert)
    }

    var srv_certpool = x509.NewCertPool()
    for i := range s.srv_certs {
        certBytes, err := ioutil.ReadFile(s.srv_certs[i])
        if err != nil {
            continue
        }
        if !srv_certpool.AppendCertsFromPEM(certBytes) {
        }
    }

    tls_config := &tls.Config{
        Certificates: client_certs,
        ClientAuth:   tls.RequireAndVerifyClientCert,

        RootCAs:    RootCAs,
        MinVersion: tls.VersionTLS12,
    }
    return tls_config
}

func (s *tls_client) Dial(addr string) (net.Conn, error) {
    return tls.Dial("tcp", addr, s.TLSConfig())
}
